Ruby 日記 9日目: 継承順序とmethod_missing
次のコードを実行するとどうなりますか
code:gold/ex09/main.rb
module M
def method_missing(id, *args)
puts "M#method_missing"
end
end
class A
include M
def method_missing(id, *args)
puts "A#method_missing"
end
end
class B < A
def method_missing(id, *args)
puts "B#method_missing"
end
end
obj = B.new
obj.dummy_method
選択肢:
エラーになる
A#method_missing
M#method_missing
B#method_missing
解説:
method_missing は呼ばれたメソッドが存在しない場合に呼ばれるメソッドだね
呼びだされたメソッドが定義されていなかった時、Rubyインタプリタがこのメソッド を呼び出します。
呼び出しに失敗したメソッドの名前 (Symbol) が name に その時の引数が第二引数以降に渡されます。
ふむふむ。どのメソッドの呼び出しに失敗したのかは name を見れば分かるんだね
で、あとは継承順序を理解していれば回答できる
AクラスはMモジュールをインクルードしている → [A, M, ...]
BクラスはAクラスを継承している → [B, A, ...]
なので、[B, A, M, ...] という順序になる
正解は B#method_missing !
code:sh
# ruby gold/ex09/main.rb
B#method_missing
/icons/hr.icon
[注意] このメソッドを override する場合は対象のメソッド名に対して Object#respond_to? が真を返すようにしてください。 そのためには、Object#respond_to_missing? も同様に override する必 要があります。
この注意事項について理解を深めたい
オブジェクトがメソッド name を持つとき真を返します。
そうだね
メソッドが定義されていない場合は、Object#respond_to_missing? を呼 び出してその結果を返します。
なるほど?
自身が symbol で表されるメソッドに対し BasicObject#method_missing で反応するつもりならば真を返します。
ほうほう。「反応するつもりならば」?
BasicObject#method_missing を override した場合にこのメソッドも override されるべきです。
ここがよくわかってない
ここまでを整理すると:
BasicObject#method_missing を override する場合は Object#respond_to? が真を返すようにしなければならない(?)
Object#respond_to? はメソッドが定義されていない場合は Object#respond_to_missing? を呼び出す
Object#respond_to_missing? は BasicObject#method_missing で反応するつもりならば(= レシーバにmethod_missingが定義されていないならば、と理解)、真を返す。そうでなければ偽を返す。
BasicObject#method_missing を override する場合は Object#respond_to? が真を返すようにしなければならない(?)
ここが一番よくわかってない。「真を返すようにしてください。 」って書いてあるけど理由がわからない。
今回の問題でも、respond_to?は偽を返すね
code:gold/ex09/sample01.rb
module M
def method_missing(id, *args)
puts "M#method_missing"
end
end
class A
include M
def method_missing(id, *args)
puts "A#method_missing"
end
end
class B < A
def method_missing(id, *args)
puts "B#method_missing"
end
end
obj = B.new
p obj.respond_to?(:dummy_method)
code:sh
# ruby gold/ex09/sample01.rb
false
ためしに respond_to_missing? を override してみようか?
code:rb
module M
def method_missing(id, *args)
puts "M#method_missing"
end
end
class A
include M
def method_missing(id, *args)
puts "A#method_missing"
end
end
class B < A
def method_missing(id, *args)
puts "B#method_missing"
end
def respond_to_missing?(_symbol, _include_private)
true
end
end
obj = B.new
p obj.respond_to?(:dummy_method)
obj.dummy_method
code:sh
# ruby gold/ex09/sample02.rb
true
B#method_missing
obj.respond_to?(:dummy_method) が真を返すようになったね。それはそう。
obj.dummy_method の返り値は変化なし。それはそう。
[注意] このメソッドを override する場合は対象のメソッド名に対して Object#respond_to? が真を返すようにしてください。
これって、なんかマナー的なものなのかな?という気がしてきた。
この問題だと、実質的にはBクラスにたいしてdummy_methodの返り値を独自に定義している、という解釈もできるわけで、それは「Bクラスのインスタンスがメソッド dummy_method を持つ」という捉え方もできるのかもしれない?(?)